﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Framework.Common.ConsistentHash
{
    /// <summary>
    /// This is a consistent hash node locator.
    /// </summary>
    public static class NodeLocator
    {
        ///// <summary>
        ///// Locates a node in a node space, faster than b
        ///// </summary>
        //  /// <param name="space">A node space contains all active nodes</param>
        //  /// <param name="Key">Un hashed string key to locate a node in a space</param>
        //  /// <returns> <see cref="T:Node" /></returns>
        // public static Node Locate(Space space, string Key)
        // {
        //     uint[] keys = space.Keys;
        //     if (!space.IsInitialized)
        //         throw new InvalidOperationException("You must initialize the space first");

        //     if (Key == null)
        //         throw new ArgumentNullException("key");

        //     if (keys.Length == 0)
        //         return null;

        //     uint hashKey = FNVHash.Calculate(Key);

        //     int start = 0;
        //     int index = keys.Length - 1;
        //     if (keys[index] < hashKey || keys[0] > hashKey)
        //     {
        //         index = 0;
        //     }
        //     else
        //     {
        //         int mid = start;
        //         while (index - start > 1)
        //         {
        //             mid = (index + start) / 2;
        //             if (keys[mid] >= hashKey)
        //                 index = mid;
        //             else
        //                 start = mid;

        //         }

        //     }
        //     return space.Circle[keys[index]];
        // }


        /// <summary>
        /// Locates a node in a node space, faster than b
        /// </summary>
        /// <param name="space">A node space contains all active nodes</param>
        /// <param name="key">Un hashed string key to locate a node in a space</param>
        /// <returns> <see cref="T:Node"></see>
        /// </returns>
        public static Node Locate(NodeList<Node> space, string key)
        {
            uint hashKey = HashFactory.Calculate(key);
            while (space.Nodes.Count >= 1)
            {
                uint[] keys = space.Keys;

                if (key == null)
                    throw new ArgumentNullException("key");

                if (keys.Length == 0)
                    return null;

                int start = 0;
                int index = keys.Length - 1;
                if (keys[index] < hashKey || keys[0] > hashKey)
                {
                    index = 0;
                }
                else
                {
                    while (index - start > 1)
                    {
                        int mid = (index + start) / 2;
                        if (keys[mid] >= hashKey)
                            index = mid;
                        else
                            start = mid;
                    }
                }

                space = space.Circle[keys[index]];
            }

            return (Node)space;
        }


        /// <summary>
        /// Using bitwise complement operation to find the hash key index in the keys, it is slower than the bove method
        /// </summary>
        /// <param name="space">A node space contains all active nodes</param>
        /// <param name="key">Un hashed string key to locate a node in a space</param>
        /// <returns> <see>
        ///               <cref>T:Node</cref>
        ///           </see>
        /// </returns>
        public static Node SLocate(Node space, string key)
        {
            uint[] keys = space.Keys;

            if (key == null)
                throw new ArgumentNullException("key");

            if (keys.Length == 0)
                return null;

            uint hashKey = HashFactory.Calculate(key);


            int index = Array.BinarySearch(keys, hashKey);
            if (index < 0)
            {
                index = ~index;
                if (index == 0)
                {
                    index = keys.Length - 1;
                }
                else if (index >= keys.Length)
                {
                    index = 0;
                }
            }
            return space.Circle[keys[index]];
        }

    }
}
